﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Micro.Extensions.Cache
{
    /// <summary>
    /// 缓存操作Client
    /// </summary>
    public interface ICacheClient
    {
        #region get
        /// <summary>
        /// 异步获取缓存
        /// </summary>
        /// <typeparam name="T">缓存返回对象类型</typeparam>
        /// <param name="key">缓存Key</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <param name="slidingExpiredSeconds">滑动过期超时时间</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<T>> GetAsync<T>(string key, bool isSliding = false, int slidingExpiredSeconds = 300);


        /// <summary>
        /// 异步获取缓存
        /// </summary>
        /// <typeparam name="T">缓存返回对象类型</typeparam>
        /// <param name="key">缓存Key</param>
        /// <param name="func">数据库加载数据委托。当缓存不存在，则执行该从数据库加载的委托，将该委托得到的结果对象重新Set到缓存中</param>
        /// <param name="expiresIn">重新Set缓存的超时时间，当isSliding为True，该值同时为滑动过期超时时间</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<T>> GetAsync<T>(string key, Func<Task<T>> func, TimeSpan expiresIn, bool isSliding = false);

        /// <summary>
        /// 异步获取缓存
        /// </summary>
        /// <typeparam name="T">缓存返回对象类型</typeparam>
        /// <param name="key">缓存Key</param>
        /// <param name="func">数据库加载数据委托。当缓存不存在，则执行该从数据库加载的委托，将该委托得到的结果对象重新Set到缓存中</param>
        /// <param name="expiresIn">重新Set缓存的超时时间，当isSliding为True，该值同时为滑动过期超时时间</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<T>> GetOrAddAsync<T>(string key, Func<Task<T>> func, TimeSpan expiresIn, bool isSliding = false);


        /// <summary>
        /// 异步获取缓存
        /// </summary>
        /// <typeparam name="T">缓存返回对象类型</typeparam>
        /// <param name="key">缓存Key</param>
        /// <param name="func">数据库加载数据委托。当缓存不存在，则执行该从数据库加载的委托，将该委托得到的结果对象重新Set到缓存中</param>
        /// <param name="expiredSeconds">重新Set缓存的超时时间，当isSliding为True，该值同时为滑动过期超时时间</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<T>> GetAsync<T>(string key, Func<Task<T>> func, int expiredSeconds, bool isSliding = false);


        /// <summary>
        /// 异步获取缓存
        /// </summary>
        /// <typeparam name="T">缓存返回对象类型</typeparam>
        /// <param name="key">缓存Key</param>
        /// <param name="func">数据库加载数据委托。当缓存不存在，则执行该从数据库加载的委托，将该委托得到的结果对象重新Set到缓存中</param>
        /// <param name="expiredSeconds">重新Set缓存的超时时间，当isSliding为True，该值同时为滑动过期超时时间</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<T>> GetOrAddAsync<T>(string key, Func<Task<T>> func, int expiredSeconds, bool isSliding = false);

        #endregion

        #region get string

        /// <summary>
        /// 异步获取缓存缓存字符串（底层不做反序列化，直接返回Redis存储string字符串）
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <param name="slidingExpiredSeconds">滑动过期超时时间</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<string>> GetStringAsync(string key, bool isSliding = false, int slidingExpiredSeconds = 300);

        /// <summary>
        /// 异步获取缓存缓存字符串（底层不做反序列化，直接返回Redis存储string字符串）
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="func">数据库加载数据委托。当缓存不存在，则执行该从数据库加载的委托，将该委托得到的结果对象重新Set到缓存中</param>
        /// <param name="expiresIn">重新Set缓存的超时时间，当isSliding为True，该值同时为滑动过期超时时间</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<string>> GetStringAsync(string key, Func<Task<string>> func, TimeSpan expiresIn, bool isSliding = false);

        /// <summary>
        /// 异步获取缓存缓存字符串（底层不做反序列化，直接返回Redis存储string字符串）
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="func">数据库加载数据委托。当缓存不存在，则执行该从数据库加载的委托，将该委托得到的结果对象重新Set到缓存中</param>
        /// <param name="expiresIn">重新Set缓存的超时时间，当isSliding为True，该值同时为滑动过期超时时间</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<string>> GetOrAddStringAsync(string key, Func<Task<string>> func, TimeSpan expiresIn, bool isSliding = false);

        /// <summary>
        /// 异步获取缓存缓存字符串（底层不做反序列化，直接返回Redis存储string字符串）
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="func">数据库加载数据委托。当缓存不存在，则执行该从数据库加载的委托，将该委托得到的结果对象重新Set到缓存中</param>
        /// <param name="expiredSeconds">重新Set缓存的超时时间，当isSliding为True，该值同时为滑动过期超时时间</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<string>> GetStringAsync(string key, Func<Task<string>> func, int expiredSeconds, bool isSliding = false);

        /// <summary>
        /// 异步获取缓存缓存字符串（底层不做反序列化，直接返回Redis存储string字符串）
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="func">数据库加载数据委托。当缓存不存在，则执行该从数据库加载的委托，将该委托得到的结果对象重新Set到缓存中</param>
        /// <param name="expiredSeconds">重新Set缓存的超时时间，当isSliding为True，该值同时为滑动过期超时时间</param>
        /// <param name="isSliding">是否滑动过期</param>
        /// <returns>缓存对象结果</returns>
        Task<CacheValue<string>> GetOrAddStringAsync(string key, Func<Task<string>> func, int expiredSeconds, bool isSliding = false);

        #endregion

        #region set

        /// <summary>
        /// 异步设置缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">缓存对象</param>
        /// <param name="expiresIn">过期时间（相对时间）</param>
        Task SetAsync<T>(string key, T value, TimeSpan expiresIn);

        /// <summary>
        /// 异步设置缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">缓存对象</param>
        /// <param name="expiredSeconds">过期时间（单位 秒）</param>
        Task SetAsync<T>(string key, T value, int expiredSeconds = 300);
        #endregion

        #region set string

        /// <summary>
        /// 异步设置缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">自定义序列化字符串</param>
        /// <param name="expiresIn">过期时间（相对时间）</param>
        Task SetStringAsync(string key, string value, TimeSpan expiresIn);

        /// <summary>
        /// 异步设置缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">自定义序列化字符串</param>
        /// <param name="expiredSeconds">过期时间（单位 秒）</param>
        Task SetStringAsync(string key, string value, int expiredSeconds = 300);

        #endregion

        #region remove
        /// <summary>
        /// 异步删除单个缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        Task RemoveAsync(string key);

        /// <summary>
        /// 异步批量删除多个缓存
        /// </summary>
        /// <param name="keys">key集合</param>
        /// <returns>返回受影响的行数</returns>
        Task<long> RemoveAsync(string[] keys);

        #endregion

        #region exist
        /// <summary>
        /// 异步判断缓存Key是否存在
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns>是否存在</returns>
        Task<bool> ExistsAsync(string key);
        #endregion

        #region refresh
        /// <summary>
        /// 异步刷新缓存
        /// </summary>
        /// <typeparam name="T">缓存对象类型</typeparam>
        /// <param name="key">Key</param>
        /// <param name="value">缓存对象</param>
        /// <param name="expiration">超时时间</param>
        Task RefreshAsync<T>(string key, T value, TimeSpan expiration);

        /// <summary>
        /// 异步刷新缓存
        /// </summary>
        /// <typeparam name="T">缓存对象类型</typeparam>
        /// <param name="key">Key</param>
        /// <param name="value">缓存对象</param>
        /// <param name="expiredSeconds">超时时间</param>
        Task RefreshAsync<T>(string key, T value, int expiredSeconds);

        #endregion

        #region flush 敏感接口，移除。

        ///// <summary>
        ///// 清空所有缓存(仅清除当前前缀下所有Key)
        ///// 注意：由于缓存客户端采用租户模式（不同系统配置了不同的Key前缀），所以当前操作底层实现为循环删除当前租户下的所有Key，时间复杂度为O(n)，
        ///// 其中n为当前租户下key的个数。
        ///// </summary>
        //void Flush();

        ///// <summary>
        ///// 异步清空所有缓存(仅清除当前前缀下所有Key)
        ///// 注意：由于缓存客户端采用租户模式（不同系统配置了不同的Key前缀），所以当前操作底层实现为循环删除当前租户下的所有Key，时间复杂度为O(n)，
        ///// 其中n为当前租户下key的个数。
        ///// </summary>
        //Task FlushAsync();
        #endregion


        Task<long> IncrementAsync(string key, long value = 1);

        Task<long> DecrementAsync(string key, long value = 1);

        Task<bool> KeyExpireAsync(string key, TimeSpan expiresIn);

        #region Hash集合操作

        Task<long> HashIncrementAsync(string key, string field, long value = 1);


        Task<long> HashDecrementAsync(string key, string field, long value = 1);


        Task<CacheValue<T[]>> HashGetAsync<T>(string key, params string[] fields);


        Task<CacheValue<KeyValuePair<string, T>[]>> HashGetAllAsync<T>(string key);


        Task HashSetAsync<T>(string key, KeyValuePair<string, T>[] keyValuePairs);


        Task HashRemoveAsync(string key, string field);


        Task<bool> HashExistsAsync(string key, string field);

        #endregion



#if DEBUG
        string BuildKey(string key);
#endif
    }
}
